"""
Redis客户端工具
"""

import redis
import redis.asyncio as aioredis
import json
import pickle
from typing import Any, Optional, Union, Dict, List
from datetime import timedelta
import logging

from app.config import settings

logger = logging.getLogger(__name__)


class RedisClient:
    """Redis客户端封装"""
    
    def __init__(self):
        self.client = redis.Redis.from_url(
            settings.REDIS_URL,
            decode_responses=True,
            socket_connect_timeout=5,
            socket_timeout=5,
            retry_on_timeout=True,
            health_check_interval=30
        )
        
        # 用于存储二进制数据的客户端
        self.binary_client = redis.Redis.from_url(
            settings.REDIS_URL,
            decode_responses=False,
            socket_connect_timeout=5,
            socket_timeout=5
        )
    
    def ping(self) -> bool:
        """检查Redis连接"""
        try:
            return self.client.ping()
        except Exception as e:
            logger.error(f"Redis连接失败: {e}")
            return False
    
    def set(self, key: str, value: Any, ex: Optional[int] = None) -> bool:
        """设置键值"""
        try:
            if isinstance(value, (dict, list)):
                value = json.dumps(value, ensure_ascii=False)
            elif not isinstance(value, str):
                value = str(value)
            
            return self.client.set(key, value, ex=ex)
        except Exception as e:
            logger.error(f"Redis设置失败 {key}: {e}")
            return False
    
    def get(self, key: str, default: Any = None) -> Any:
        """获取键值"""
        try:
            value = self.client.get(key)
            if value is None:
                return default
            
            # 尝试解析JSON
            try:
                return json.loads(value)
            except (json.JSONDecodeError, TypeError):
                return value
        except Exception as e:
            logger.error(f"Redis获取失败 {key}: {e}")
            return default
    
    def delete(self, *keys: str) -> int:
        """删除键"""
        try:
            return self.client.delete(*keys)
        except Exception as e:
            logger.error(f"Redis删除失败 {keys}: {e}")
            return 0
    
    def exists(self, key: str) -> bool:
        """检查键是否存在"""
        try:
            return self.client.exists(key) > 0
        except Exception as e:
            logger.error(f"Redis检查存在失败 {key}: {e}")
            return False
    
    def expire(self, key: str, seconds: int) -> bool:
        """设置过期时间"""
        try:
            return self.client.expire(key, seconds)
        except Exception as e:
            logger.error(f"Redis设置过期失败 {key}: {e}")
            return False
    
    def ttl(self, key: str) -> int:
        """获取剩余生存时间"""
        try:
            return self.client.ttl(key)
        except Exception as e:
            logger.error(f"Redis获取TTL失败 {key}: {e}")
            return -1
    
    def incr(self, key: str, amount: int = 1) -> int:
        """递增"""
        try:
            return self.client.incr(key, amount)
        except Exception as e:
            logger.error(f"Redis递增失败 {key}: {e}")
            return 0
    
    def decr(self, key: str, amount: int = 1) -> int:
        """递减"""
        try:
            return self.client.decr(key, amount)
        except Exception as e:
            logger.error(f"Redis递减失败 {key}: {e}")
            return 0
    
    def mget(self, keys: List[str]) -> List[Any]:
        """批量获取"""
        try:
            values = self.client.mget(keys)
            result = []
            for value in values:
                if value is None:
                    result.append(None)
                else:
                    try:
                        result.append(json.loads(value))
                    except (json.JSONDecodeError, TypeError):
                        result.append(value)
            return result
        except Exception as e:
            logger.error(f"Redis批量获取失败 {keys}: {e}")
            return [None] * len(keys)
    
    def mset(self, mapping: Dict[str, Any]) -> bool:
        """批量设置"""
        try:
            processed_mapping = {}
            for key, value in mapping.items():
                if isinstance(value, (dict, list)):
                    processed_mapping[key] = json.dumps(value, ensure_ascii=False)
                elif not isinstance(value, str):
                    processed_mapping[key] = str(value)
                else:
                    processed_mapping[key] = value
            
            return self.client.mset(processed_mapping)
        except Exception as e:
            logger.error(f"Redis批量设置失败: {e}")
            return False
    
    def keys(self, pattern: str = "*") -> List[str]:
        """获取匹配的键"""
        try:
            return self.client.keys(pattern)
        except Exception as e:
            logger.error(f"Redis获取键失败 {pattern}: {e}")
            return []
    
    def scan_iter(self, match: str = "*", count: int = 1000):
        """迭代扫描键"""
        try:
            return self.client.scan_iter(match=match, count=count)
        except Exception as e:
            logger.error(f"Redis扫描失败 {match}: {e}")
            return iter([])
    
    def pipeline(self):
        """创建管道"""
        return self.client.pipeline()
    
    def set_object(self, key: str, obj: Any, ex: Optional[int] = None) -> bool:
        """存储Python对象（使用pickle）"""
        try:
            serialized = pickle.dumps(obj)
            return self.binary_client.set(key, serialized, ex=ex)
        except Exception as e:
            logger.error(f"Redis存储对象失败 {key}: {e}")
            return False
    
    def get_object(self, key: str, default: Any = None) -> Any:
        """获取Python对象（使用pickle）"""
        try:
            serialized = self.binary_client.get(key)
            if serialized is None:
                return default
            return pickle.loads(serialized)
        except Exception as e:
            logger.error(f"Redis获取对象失败 {key}: {e}")
            return default
    
    def set_hash(self, name: str, mapping: Dict[str, Any]) -> bool:
        """设置哈希"""
        try:
            processed_mapping = {}
            for key, value in mapping.items():
                if isinstance(value, (dict, list)):
                    processed_mapping[key] = json.dumps(value, ensure_ascii=False)
                elif not isinstance(value, str):
                    processed_mapping[key] = str(value)
                else:
                    processed_mapping[key] = value
            
            return self.client.hset(name, mapping=processed_mapping)
        except Exception as e:
            logger.error(f"Redis设置哈希失败 {name}: {e}")
            return False
    
    def get_hash(self, name: str) -> Dict[str, Any]:
        """获取哈希"""
        try:
            hash_data = self.client.hgetall(name)
            result = {}
            for key, value in hash_data.items():
                try:
                    result[key] = json.loads(value)
                except (json.JSONDecodeError, TypeError):
                    result[key] = value
            return result
        except Exception as e:
            logger.error(f"Redis获取哈希失败 {name}: {e}")
            return {}
    
    def get_info(self) -> Dict[str, Any]:
        """获取Redis信息"""
        try:
            return self.client.info()
        except Exception as e:
            logger.error(f"Redis获取信息失败: {e}")
            return {}
    
    def flushdb(self) -> bool:
        """清空当前数据库"""
        try:
            return self.client.flushdb()
        except Exception as e:
            logger.error(f"Redis清空数据库失败: {e}")
            return False


# 创建全局Redis客户端实例
redis_client = RedisClient()

# 创建异步Redis客户端
async_redis_client = aioredis.from_url(
    settings.REDIS_URL,
    decode_responses=True,
    socket_connect_timeout=10,  # 增加连接超时
    socket_timeout=10,          # 增加socket超时
    retry_on_timeout=True,
    health_check_interval=60,   # 增加健康检查间隔
    socket_keepalive=True,      # 启用keepalive
    socket_keepalive_options={}
)


def get_redis_client() -> RedisClient:
    """获取同步Redis客户端实例"""
    return redis_client


async def get_async_redis_client():
    """获取异步Redis客户端实例"""
    return async_redis_client


async def test_async_redis_connection() -> bool:
    """测试异步Redis连接"""
    try:
        await async_redis_client.ping()
        return True
    except Exception as e:
        logger.error(f"异步Redis连接失败: {e}")
        return False
